﻿/*
ParallelPlatform Project - A web platform to distribute and share knowledge
Online Editor - An online tool to create and play animations
Copyright (C) 2011 Daniel Guerrero Martínez & Sergio Rodríguez Lumley

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package Classes{
	import flash.display.MovieClip;
	import flash.utils.getDefinitionByName;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	public class Canvas extends MovieClip {
		
		private static const NO_AUTHOR:String = "@Xonline_editor_no_authorX@";
		// Container, keeps all ShapeObjects of the Canvas
		private var movieTitle:String;
		private var description:String;
		private var delay:Number; //Number of seconds a scene lasts
		private var authors:Array; // List of authors
		private var currentAuthor:String;
		
		//private var toolinfo:ToolInfo; // Keeps information about tools
		private var changeToolFunction:Function;
		private var changeToolRequested:Boolean;
		private var tool:uint; // Tool selected to apply actions
		private var handlers:Array; // List of handlers
		private var handler:CanvasHandlerInterface; // Current handler
		
		private var core:CanvasCore;
		
		/**
		Constructor
		@param id int with the value of the ID, 0 by default
		*/
		public function Canvas(){
			super();
			
			x 				= 0;
			y 				= 0;
			
			currentAuthor	= NO_AUTHOR;
			authors			= new Array();
			tool			= ToolInfo.SELECT;
			movieTitle 		= "New movie";
			description		= "No description added to this animation";
			delay			= 2; // 2 seconds
			changeToolRequested = false;
			this.mouseChildren = true;
			
			core			= new CanvasCore(this);
			
			handlers		= new Array(5); // Number of handlers
			handlers[0]		= new CanvasSelectHandler(core);
			handlers[1]		= new CanvasInsertHandler(core);
			handlers[2]		= new CanvasRemoverHandler(core);
			handlers[3]		= new CanvasTextHandler(core);
			handlers[4]		= new CanvasArrowHandler(core);
			handlers[5]		= new CanvasBlurHandler(core);
			handlers[6]		= new CanvasPaintHandler(core);
			handler		= handlers[0];
			
			addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
			addEventListener(MouseEvent.MOUSE_UP, mouseUp);
			//this.addEventListener(MouseEvent.DOUBLE_CLICK, shapeEdit); // Shapes take care of their own
			
		}
		
		/**
		  Sets the title of the movie
		  @param str String with the new title of the movie
		*/
		public function setTitle(str:String):void{
			movieTitle = str;
		}
		
		/**
		  Returns the movie title
		  @return String with the movie title
		*/
		public function getTitle():String{
			return movieTitle;
		}
		
		/**
		  Sets the delay of the movie
		  @param num Number with the delay of the movie
		*/
		public function setDelay(num:Number):void{
			delay = num;
		}
		
		/**
		  Returns the delay of the movie
		  @return Number with the delay of the movie
		*/
		public function getDelay():Number{
			return delay
		}
		
		/**
		  Adds the author to the list of colaborators in case it wasn't
		  added before
		  @param auth String with the author's name or ID
		*/
		public function addAuthor(auth:String):void{
			currentAuthor = auth;
			this.setAuthors(authors);
		}
		
		/**
		  Sets the Description of the movie
		  @param str String with the new Description of the movie
		*/
		public function setDescription(str:String):void{
			description = str;
		}
		
		/**
		  Returns the movie Description
		  @return String with the movie Description
		*/
		public function getDescription():String{
			return description;
		}
		
		/**
		  Sets the undo and redo functions to be called when there is an action
		  to undo (or not), or to redo (or not)
		  @param undF Function to undo action, with one parameter (Boolean)
		  @param redF Function to redo action, with one parameter (Boolean)
		*/
		public function setUndoRedoFunctions(undF:Function, redF:Function):void{
			core.setUndoRedoFunctions(undF, redF);
		}
		
		/**
		  Sets the change tool function, this is a method to allow the
		  canvas to request the change of the tool to a different tool and
		  automatically edit the last shape (in case of select tool)
		  @param selF Function to call to request the change of the tool
		*/
		public function setChangeToolFunction(selF:Function):void{
			changeToolFunction = selF;
			core.setChangeToolFunction(changeTool);
		}
		
		/**
		  Detects when a key has been pressed
		*/
		public function keyPressed(evt:KeyboardEvent):void{
			handler.keyPressed(evt);
		}
		
		/**
		  Redoes the last undone action (if there is one)
		*/
		public function redoAction():void{
			if(handler.hasRecord()){
				handler.redo();
			}else{
				handler.deactivate();
				core.redo();
				handler.activate();
			}
		}
		
		/**
		  Undoes the last action
		*/
		public function undoAction():void{
			if(handler.hasRecord()){
				handler.undo();
			}else{
				handler.deactivate();
				core.undo();
				handler.activate();
			}
		}
		
		/**
		  Sets the tool to apply to the canvas
		  @param t String with the tool to apply, according to "ToolInfo" class
		  @param shapeobj Object specified with a String, or "null"
		 */
		public function toolToApply(t:uint, shapeobj:String){
		
			if(tool != t){
				handler.deactivate();
			}
			
			tool = t;
			
			switch(tool){
				case ToolInfo.INSERT:
					handler = handlers[1];
					handler.activate(shapeobj);
					break;
				case ToolInfo.ARROW:
					handler = handlers[4];
					handler.activate(shapeobj);
					break;
				case ToolInfo.TEXT:
					handler = handlers[3];
					handler.activate(shapeobj);
					break;
				case ToolInfo.SELECT:
					handler = handlers[0];
					handler.activate();
					
					if(changeToolRequested){ // If select has been requested, we edit the text of the last element added
						changeToolRequested = false;
						(handler as CanvasSelectHandler).requestSelection();
					}
					break;
				case ToolInfo.REMOVE:
					handler = handlers[2];
					handler.activate();
					break;
					
				case ToolInfo.BLUR:
					handler = handlers[5];
					handler.activate();
					break;
					
				case ToolInfo.CREATE:
					handler = handlers[6];
					handler.activate(shapeobj);
					break;
			}
			
			if(handler.hasRecord()){
				handler.checkUndoRedo();
			}else{
				core.checkUndoRedoActions();
			}
			
			// In case another tool has been requested, we just ignore
			// this variable
			if(changeToolRequested) changeToolRequested = false;
		}
		
		/**
		  Selects the specified container to show in the canvas and edit
		  @param c Int specifying the container to select
		*/
		public function selectContainer(c:int):void{
			handler.deactivate();
			core.selectContainer(c);
			handler.activate();
		}
		
		/**
		  Creates from scratch the list of containers according to the number
		  of scenes specified. Before doing so, it clears the current list of containers.
		  It sets cont (current container) to zero.
		*/
		public function createContent(numScenes:uint):void{
			handler.deactivate();
			this.setAuthors(authors);
			core.createContent(numScenes);
		}
		
		/**
		  Checks for consistency between all containers, from the one
		  selected right now to the end, creating shapes, destroying,
		  moving them... depending on the actions done
		  @param buildAll Boolean indicates if its necessary to build the whole
		  			animation (used in readXML)
		*/
		public function buildContainers(buildAll:Boolean = false):void{
			handler.deactivate();
			core.buildContainers(buildAll);
			handler.activate();
		}
		
		/**
		  Returns a reference to the vector of containers
		*/
		public function getContainers():Vector.<Container>{
			// Rebuild all the containers before returning them!
			buildContainers();
			return core.getContainers();
		}
		
		/**
		  Returns an unsigned int with the index of the current container
		*/
		public function getCurrentContainerIndex():uint{
			return core.getCurrent();
		}
		
		/**
		  Creates or destroys containers in order to set the amount
		  of current containers to the specified number. If containers
		  are to be created, they are pushed at the end.
		  Warning! If current container is popped it is not changed
		  automatically, but it is safely removed
		  @param num uint with the new length of containers
		*/
		public function setNumContainers(num:uint):void{
			while(core.getNumContainers() > num){
				if(core.getCurrent() == core.getNumContainers()-1){
					handler.deactivate();
				}
				core.popContainer();
			}
			
			while(core.getNumContainers() < num){
				core.createContainer();
			}
		}
		
		/**
		  Returns the number of scenes (called containers here)
		*/
		public function getNumContainers():uint{
			return core.getNumContainers();
		}
		
		/**
		  Sets the type of each container given a list of types
		  @param types Vector.<uint> with each type of Container.type
		*/
		public function setContainersTypes(types:Vector.<uint>):void{
			core.setContainersTypes(types);
		}
		
		
		/**
		  Reads an XML file and builds the whole animation from it
		  @param xmlFile XML with all the contents of the animation
		  @return int with an error code asociated, 0 if no error
		*/
		public function readXML(xmlFile:XML):int{
			var error:int = 0; //No error
			var sceneList:XMLList;
			
			this.setTitle(FileModule.getTitle(xmlFile));
			this.setDelay(FileModule.getDelay(xmlFile));
			this.setDescription(FileModule.getDescription(xmlFile));
			this.setAuthors(FileModule.getAuthors(xmlFile));
			
			handler.deactivate();
			sceneList = FileModule.getSceneList(xmlFile);
			
			core.readXML(sceneList);
			this.selectContainer(0);

			return error;
		}
		
		/**
		  Builds an XML file as a String from the current project
		  @return String with all the actions
		*/
		public function writeXML():String{
			var xmlStr:String = FileModule.writeHeader(movieTitle, delay, description, authors);
			xmlStr += FileModule.writeBody(core.getContainers());
			xmlStr += FileModule.writeDocumentFoot();
			return xmlStr;
		}
		
		public function printActions():void{
			trace("++         -----------------        ++");
			//for(var i:uint = 0; i< containers.length; ++i){
			//	trace("Actions "+String(i));
				//trace(containers[i].getActions().printString());
			//	trace(containers[i].printActions());
			//}
			trace(core.getContainer(core.getCurrent()).printActions());
		}
		
		/**
		 Function called from ShapeObjects, selects the correct action to do depending
		 on the type of click
		 @param evt MouseEvent
		 @param obj ShapeObject which triggered the call
		 @param type Type defined statically in ShapeObject
		*/
		public function clickShape(evt:MouseEvent, obj:ShapeObject, type:int):void{
			handler.clickShape(evt, obj, type);
		}
		
		/************** PROTECTED FUNCTIONS *****************/
		
		/**
		  Detects when clicking on the front panel, over everything else,
		  depending on the program status and selected tool, destroys the
		  text_edit or adds shapes
		  @param evt MouseEvent
		*/
		function clickFront(evt:MouseEvent):void{
			handler.clickFront(evt);
		}
		
		/**
		  Detects when nothing in the stage has been clicked,
		  unselects all the objects only if Shift was not pressed
		  @param evt MouseEvent
		*/
		function clickBack(evt:MouseEvent):void{
			handler.clickBack(evt);
		}
		
		/************** PRIVATE FUNCTIONS *****************/
		
		/**
		  Requests to change the tool to canva's parent
		*/
		private function changeTool(t:uint):void{
			changeToolRequested = true;
			changeToolFunction(t);
		}
		
		/**
		  A shape or a set of shapes are being dragged, we move it to the coordinates
		  of the mouse
		  @param evt MouseEvent
		*/
		private function mouseMove(evt:MouseEvent):void{
			handler.mouseMove(evt);
		}
		
		/**
		  Mouse button has been lifted, we stop dragging
		*/
		private function mouseUp(evt:MouseEvent):void{
			handler.mouseUp(evt);
		}
		
		/**
		  Sets the array of authors, including the current author (if there is one)
		*/
		private function setAuthors(auths:Array):void{
			authors = auths;
			if(currentAuthor != NO_AUTHOR){
				var found:Boolean = false;
				// Add the current author, search if it was already included
				for (var i:uint = 0; i<authors.length && !found; ++i){
					if(authors[i] == currentAuthor){
						found = true;
					}
				}
				if(!found){
					authors.push(currentAuthor);
				}
			}
		}

	}
}